/*!
 Temelia - Linked list samples.

 Copyright (C) 2008 Ceata (http://ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "include/samples.h"
#include <temelia/linked_list.h>

struct interval
{
	double inf;
	double sup;
};

static void print_interval(void *x, void *fout)
{
	struct interval *in = (struct interval *) x;
	PRINT("(%lf,%lf)\n", in->inf, in->sup);
}

static int compare_interval(void *x, void *y, void *context)
{
	struct interval *in1, *in2;
	in1 = (struct interval *) x;
	in2 = (struct interval *) y;
	return (int) (in1->inf - in2->inf);
}

static void sample1()
{
	linked_list_t list1, list2, list3;
	int *x, *y, n, i;

	PRINT("\n\n Sample 1 started \n\n");

	n = 100;
	list1 = linked_list_new();
	list2 = linked_list_new();

	for (i = 0; i < n; i++)
	{
		x = int_new(i);
		y = int_new(i * i);

		linked_list_push_back(list1, x);
		linked_list_push_front(list2, y);
	}

	PRINT("Before reverse :\n");
	linked_list_iterate(list1, int_handler, NULL, 1);

	linked_list_reverse(list1);

	PRINT("\nAfter reverse :\n");
	linked_list_iterate(list1, int_handler, NULL, 1);

	PRINT("\n\nMerging size1 = %d, size2 = %d:\n", linked_list_get_size(list1),
			linked_list_get_size(list2));

	linked_list_sort(list1, int_compare, NULL);
	linked_list_sort(list2, int_compare, NULL);

	PRINT("\nList 1:\n");
	linked_list_iterate(list1, int_handler, NULL, 1);
	PRINT("\nList 2:\n");
	linked_list_iterate(list2, int_handler, NULL, 1);

	PRINT("\nAfter merge :\n");
	list3 = linked_list_merge(list1, list2, int_compare, NULL);
	linked_list_iterate(list3, int_handler, NULL, 1);

	PRINT("\nFront/Back: %d %d\n", *(int *) linked_list_get_front(list3),
			*(int *) linked_list_get_back(list3));

	linked_list_iterate(list1, int_delete, NULL, 1);
	linked_list_iterate(list2, int_delete, NULL, 1);

	/*
	 * You have another alternative;
	 * you can free the pointers from the third list
	 */
	//linked_list_iterate(list3, int_delete, NULL, 1);

	linked_list_delete(list1);
	linked_list_delete(list2);
	linked_list_delete(list3);

	PRINT("\n\n Sample 1 ended \n\n");
}

static void sample2()
{
	linked_list_t list;
	linked_list_iterator_t it;
	int i, *x, N = 500, *y, *z;

	PRINT("\n\n Sample 2 started \n\n");

	it = (linked_list_iterator_t) 0x12345678; // Random memory address

	list = linked_list_new();

	y = int_new(3);
	z = int_new(6);

	for (i = 0; i < N; i++)
	{
		x = int_new(i);
		linked_list_push_back(list, x);
	}

	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");
	linked_list_iterate(list, int_handler, NULL, -1);
	PRINT("\n");

	PRINT("%d %d\n", linked_list_check(list, linked_list_iterator_get_next(linked_list_get_begin(list))),
			linked_list_check(list, it));

	PRINT("Searching for the value %d\n", *y);
	it = linked_list_search_key(list, y, int_compare, NULL);

	if (it != NULL)
	{
		PRINT("Item found ! Modifying the value .. \n");
		*y = 20;
		x = linked_list_iterator_get_key(it);
		linked_list_iterator_set_key(it, y);
		y = x;
		PRINT("The new Linked List looks like : \n");
		linked_list_iterate(list, int_handler, NULL, 1);
		PRINT("\n");
	}
	else
		PRINT("Item not found !");

	PRINT("Priting list index....\n");
	for (it = linked_list_get_begin(list); it != NULL; it
			= linked_list_iterator_get_next(it))
		PRINT("%d ", linked_list_search_iterator(list, it));

	PRINT("Priting list reversed index....\n");
	for (it = linked_list_get_end(list); it != NULL; it
			= linked_list_iterator_get_prev(list, it))
		PRINT("%d ", linked_list_search_iterator(list, it));
	PRINT("\n");

	PRINT("Special iterator %d\n", linked_list_search_iterator(list,
					linked_list_iterator_get_prev(list, NULL)));

	linked_list_iterate(list, int_delete, NULL, 1);
	linked_list_delete(list);
	free(z);
	free(y);

	PRINT("\n\n Sample 2 ended \n\n");
}

static void sample3()
{
	linked_list_t list;
	linked_list_iterator_t it;
	int i, N = 10;
	struct interval *x;

	PRINT("\n\n Sample 3 started \n\n");

	list = linked_list_new();

	for (i = 0; i < N; i++)
	{
		x = (struct interval *) malloc(sizeof(struct interval));
		x->inf = rand() % 10;
		x->sup = x->inf + rand() % 10;
		linked_list_push_back(list, x);
	}

	PRINT("The initial list : \n");
	linked_list_iterate(list, print_interval, NULL, 1);
	linked_list_sort(list, compare_interval, NULL);
	PRINT("\nThe list after sorting it : \n");

	for (it = linked_list_get_begin(list); it != NULL; it
			= linked_list_iterator_get_next(it))
	{
		x = (struct interval *) linked_list_iterator_get_key(it);
		PRINT("(%p, [%lf, %lf])\n", it, x->inf, x->sup);
		free(linked_list_iterator_get_key(it));
	}

	PRINT("Begin/End: %p %p\n", linked_list_get_begin(list), linked_list_get_end(list));

	linked_list_delete(list);

	PRINT("\n\n Sample 3 ended \n\n");
}

static void sample4()
{
	int *x, i, N = 10;
	linked_list_t list;
	linked_list_iterator_t it;

	PRINT("\n\n Sample 4 started \n\n");

	list = linked_list_new();

	linked_list_push_front(list, int_new(1000));
	linked_list_push_front(list, int_new(1001));
	linked_list_push_front(list, int_new(1002));
	linked_list_push_front(list, int_new(1003));

	for (i = 0; i < N; i++)
	{
		x = int_new(i + 1);
		linked_list_insert_before(list, NULL, x);
	}

	PRINT("\n");
	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");

	for (i = 0; i < N; i++)
	{
		x = int_new(N * N + i);
		linked_list_insert_before(list, linked_list_get_begin(list), x);
	}

	PRINT("After some inserting after...\n");
	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");

	linked_list_insert_after(list, linked_list_get_begin(list), int_new(12345));
	linked_list_insert_after(list, linked_list_get_end(list), int_new(12346));
	linked_list_insert_after(list, linked_list_iterator_get_next(
			linked_list_get_begin(list)), int_new(12347));
	linked_list_insert_after(list, linked_list_iterator_get_prev(list,
			linked_list_get_end(list)), int_new(12348));
	linked_list_insert_after(list, NULL, int_new(777));

	PRINT("After some insertions...\n");
	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");

	for (i = 0; i < linked_list_get_size(list) && i < N; i++)
	{
		it = linked_list_get_iterator_at(list, i);
		PRINT("[%p, %d]\n", it, *(int *) linked_list_iterator_get_key(it));
		free(linked_list_iterator_get_key(it));
	}

	for (i = 0; i < N; i++)
		linked_list_remove_iterator(list, linked_list_get_begin(list), 1);

	PRINT("\n");
	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");

	x = linked_list_get_front(list);
	linked_list_pop_front(list);
	free(x);

	x = linked_list_get_back(list);
	linked_list_pop_back(list);
	free(x);

	for (i = 0; i < N; i++)
	{
		free(linked_list_get_key_at(list, linked_list_get_size(list) - 1));
		linked_list_remove_iterator(list, linked_list_get_end(list), 1);
	}

	PRINT("\nSwapping 0 and 1...\n");
	linked_list_iterate(list, int_handler, NULL, 1);

	x = linked_list_get_key_at(list, 0);
	linked_list_set_key_at(list, 0, linked_list_get_key_at(list, 1));
	linked_list_set_key_at(list, 1, x);

	PRINT("\n");
	linked_list_iterate(list, int_handler, NULL, 1);
	PRINT("\n");

	/*
	 * If you remove a key, the iterator is removed too and the keys
	 * from right move one step to left.
	 * In this example, the second key becomes the first key
	 * after removing the front key from linked list.
	 */
	x = linked_list_get_key_at(list, 0);
	linked_list_remove_key(list, linked_list_get_key_at(list, 0), int_compare,
			NULL, 1);
	free(x);

	x = linked_list_get_key_at(list, 0);
	linked_list_remove_key(list, linked_list_get_key_at(list, 0), int_compare,
			NULL, 1);
	free(x);

	linked_list_iterate(list, int_handler, NULL, 1);
	linked_list_iterate(list, int_delete, NULL, 1);

	while (!linked_list_is_empty(list))
	{
		if (rand() % 2)
			linked_list_pop_back(list);
		else
			linked_list_pop_front(list);
	}

	// Clear the linked list
	linked_list_set_begin(list, NULL);
	linked_list_set_end(list, NULL);
	linked_list_set_size(list, 0);

	linked_list_delete(list);

	PRINT("\n\n Sample 4 ended \n\n");
}

static void sample5()
{
	linked_list_t list1, list2, list3;
	int i, N = 10;

	PRINT("\n\n Sample 5 started \n\n");

	list1 = linked_list_new();
	list2 = linked_list_new();
	list3 = linked_list_new();

	for (i = 0; i < N; i++)
	{
		linked_list_push_back(list1, int_new(i + 1));
		linked_list_push_front(list2, int_new(N + i + 1));
	}

	PRINT("list1 = ");
	linked_list_iterate(list1, int_handler, NULL, 1);

	PRINT("\nlist2 = ");
	linked_list_iterate(list2, int_handler, NULL, 1);
	PRINT("\n");

	linked_list_spice(list3, list2, NULL);

	PRINT("list3 = ");
	linked_list_iterate(list3, int_handler, NULL, 1);
	PRINT("\n");

	printf("%p %p\n", linked_list_get_end(list3),
			linked_list_iterator_get_prev(list3, linked_list_get_end(list3)));

	linked_list_spice(list3, list1, linked_list_iterator_get_prev(list3,
			linked_list_get_end(list3)));

	PRINT("list3 = ");
	linked_list_iterate(list3, int_handler, NULL, 1);
	PRINT("\n");

	linked_list_iterate(list3, int_delete, NULL, 1);

	linked_list_delete(list3);

	PRINT("\n\n Sample 5 ended \n\n");
}

void run_linked_list_samples()
{
	sample1();
	sample2();
	sample3();
	sample4();
	sample5();
}
